#!/usr/bin/env python
# -*- coding: utf-8 -*-

class Automato:
    estados = []        #Lista de Estados
    estadosFinais = []  #Lista de Estados Finais
    alfabeto = []       #Lista de Alfabeto
    transicoes = []     #Matriz de Transi??es
    estadoAtual = ""    #Estado Atual do AF
    estadoInicial = ""  #Estado Inicio do AF
    leitor = ""         #Sensor de Leitura do AF
    
    #Construtor do AF através de um arquivo
    #contendo suas especificações
    def __init__(self, arquivo):
        self.insertEstados(arquivo)
        self.insertAlfabeto(arquivo)
        self.insertTransicoes(arquivo)
        self.estadoInicial = self.estados[0]
        self.estadoAtual = self.estadoInicial
        self.leitor = None

    #Função para impressão do AF
    def printAutomato(self, arquivo):
        arquivo.write('Estados: ')
        for estado in self.estados:
            arquivo.write(estado + ' ')
        
        arquivo.write('\n\nAlfabeto: ')
        for simbolo in self.alfabeto:
            arquivo.write(simbolo + ' ')
        
        arquivo.write('\n\nEstados finais: ')
        for estado in self.estadosFinais:
            arquivo.write(estado + ' ')
        
        arquivo.write('\n\nEstado inicial: ' + self.estadoInicial)
        
        arquivo.write('\n\nTransições:\n\n')
        arquivo.write(''.rjust(8))
        for simbolo in self.alfabeto:
            arquivo.write(simbolo.rjust(8))
            
        for estado in self.estados:
            arquivo.write('\n' + estado.rjust(8))
            i = self.estados.index(estado)
            for simbolo in self.alfabeto:
                j = self.alfabeto.index(simbolo)
                try:
                    arquivo.write(self.transicoes[i][j].rjust(8))
                except:
                    arquivo.write("null".rjust(8))
    
    # Método de Inserção de Estados e Estados Finais
    def insertEstados(self, arquivo):
        estados = arquivo.readline()[:-1]
        for estado in estados.split(" "):
            if estado[0] == "*":
                self.estados.append(estado[1:])
                self.estadosFinais.append(estado[1:])
            else:
                self.estados.append(estado)

    # Método de Inserção do alfabeto
    def insertAlfabeto(self, arquivo):
        alfabeto = arquivo.readline()[:-1]
        for simbolo in alfabeto.split(" "):
            self.alfabeto.append(simbolo)

    # Método de Inserção de Transições
    def insertTransicoes(self, arquivo):
        lista_transicoes = arquivo.read().split("\n")
        
        #Criando as colunas de alfabeto    
        for j in range( len(self.estados) ):    
            self.transicoes.append([])
            for i in range( len(self.alfabeto) ): 
                self.transicoes[j].append(None)
            
        
        for transicao in lista_transicoes:
            lista_elementos = transicao.split(" ")
            estado_inicial = self.estados.index(lista_elementos[0])
            simbolo = self.alfabeto.index(lista_elementos[1])
            estado_final = lista_elementos[2]
            self.transicoes[estado_inicial][simbolo] = estado_final

    #Função para analisar a cadeia de entrada
    def analisaCadeia(self, cadeia):
        self.estadoAtual = self.estadoInicial
        semTransicao = False
        
        #
        for simbolo in cadeia:
                self.leitor = simbolo
                #Verifica se a cadeia foi lida até o fim
                '''
                A comparação abaixo funciona, mas gostaria de
                entender melhor o porquê; também gostaria que
                mudássemos o código para não utilizar o "break"
                '''
                if self.leitor == '':
                    break
                elif self.analisaSimbolo() == False:
                    semTransicao = True
                    break
        
        #Verifica se o estado atual é um estado final
        try:
            i = self.estadosFinais.index(self.estadoAtual)
        except ValueError:
            i = -1
        
        if i >= 0 and semTransicao == False:
            return True
        
        return False
    
    #Função para verificar se o símbolo deve ser aceito pelo AF
    def analisaSimbolo(self):
        proximoEstado = self.getTransicao()
        
        if proximoEstado != None:
            self.estadoAtual = proximoEstado
            return True
        return False
    
    # Método para obtenção do próximo estado do AF
    def getTransicao(self):
        i = self.estados.index(self.estadoAtual)
        j = self.alfabeto.index(self.leitor)
        
        return self.transicoes[i][j]
    
